/*
 * Copyright 2024 Hangzhou Yingyi Technology Co., Ltd
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <raspi/mbox.h>
#include <uk/assert.h>
#include <uk/ofw/fdt.h>
#include <libfdt.h>
#include <stdarg.h>

/* mailbox message buffer */
static unsigned int  __align(16) mbox[MBOX_BUFFER_LENGTH];

static uint64_t mbox_mem_addr;
static uint8_t mbox_initialized;

/* Macros to access Mailbox Registers with base address 'mbox_mem_addr'*/
#define MBOX_REG(r)		((uint32_t *)(mbox_mem_addr + (r)))
#define MBOX_REG_READ(r)	ioreg_read32(MBOX_REG(r))
#define MBOX_REG_WRITE(r, v)	ioreg_write32(MBOX_REG(r), v)

 /*mbox full return true, else return false */
static inline bool mbox_full(void)
{
	return (MBOX_REG_READ(MBOX_STATUS) & MBOX_FULL);
}

//  /*mbox empty return true, else return false */
static inline bool mbox_empty(void)
{
	return (MBOX_REG_READ(MBOX_STATUS) & MBOX_EMPTY);
}

/**
 * Make a mailbox call. Returns 0 on failure, non-zero on success
 */
int mbox_call(unsigned char ch)
{
	if (!mbox_initialized) {
		UK_CRASH("MBOX not initialized!\n");
		return -1;
	}
	/**
	 * The buffer mbox is 16-byte aligned
	 * only the upper 28 bits of the address
	 * can be passed via the mailbox. The last 4 bits
	 * take the lower 4 digits of the channel, also 16-byte aligned.
	 */
	unsigned int r = (((uint32_t)((uint64_t)&mbox)&~0xF) | (ch&0xF));
	/* wait until we can write to the mailbox */
	do {
		asm volatile("nop");
	} while (mbox_full());
	MBOX_REG_WRITE(MBOX_WRITE, r);

	barrier();
	clean_and_invalidate_dcache_range(
		(unsigned long)&mbox, MBOX_BUFFER_LENGTH*sizeof(mbox[0]));

	/* now wait for the response */
	do {
		asm volatile("nop");
	} while (mbox_empty() ||
		r != MBOX_REG_READ(MBOX_READ) || mbox[1] == MBOX_REQUEST);

	return mbox[1] == MBOX_RESPONSE_NORMAL;
}

void _libraspiplat_init_mbox(void *fdtp)
{
	int mbox_offset, rc;
	uint64_t addr, size;

	mbox_offset = fdt_node_offset_by_compatible(fdtp, -1,
						"brcm,bcm2835-mbox");

	if (mbox_offset < 0)
		UK_CRASH("No mailbox device found!\n");

	rc = fdt_get_address(fdtp, mbox_offset, 0, &addr, &size);
	if (rc < 0)
		UK_CRASH("Could not find Mailbox address!\n");

	mbox_mem_addr = addr;
	mbox_initialized = 1;
}

void mbox_msg_create(int paras_num, ...)
{
	va_list args;

	va_start(args, paras_num);

	for (int i = 0; i < paras_num && i < MBOX_BUFFER_LENGTH; ++i)
		mbox[i] = va_arg(args, uint32_t);

	va_end(args);

	for (int i = paras_num; i < MBOX_BUFFER_LENGTH; ++i)
		mbox[i] = 0;
}
